# decoration.rb
# Copyright (C) 2004-2009 Akira TAGOH

# Authors:
#   Akira TAGOH  <akira@tagoh.org>

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

require 'thread'
require 'prune/composer'
require 'prune/message'


Thread.abort_on_exception = true if $DEBUG

module PRUNE

=begin rdoc

== PRUNE::Decoration

=end

  module Decoration
    @@MessageMap = {
      'PASS'=>'',
      'NICK'=>'__TIME__*** __NICK__ -> __PARAM0__',
#      'USER'=>'',
#      'OPER'=>'',
      'MODE'=>'__TIME__*** Mode on __CHANNEL__ by (__NICK__|__HOST__): __PARAMS1__',
#      'SERVICE'=>'',
      'QUIT'=>'__TIME__*** __NICK__ has left IRC \(__PARAM0__\)',
#      'SQUIT'=>'',
      'JOIN'=>'__TIME__*** __NICK__ \(__USER__@__HOST__\) has joined __CHANNEL__',
      'PART'=>'__TIME__*** __NICK__ has left __CHANNEL__',
      'TOPIC'=>'__TIME__*** Topic by __NICK__: __PARAM1__',
#      'NAMES'=>'',
#      'LIST'=>'',
      'INVITE'=>'__TIME__*** __NICK__ invites __PARAM0__ to __PARAM1__',
      'KICK'=>'__TIME__*** __NICK__ kicked __PARAM1__ out from __CHANNEL__',
      'PRIVMSG'=>'__TIME__<__CHANNEL__:__NICK__> __PARAM1__',
      'NOTICE'=>'__TIME__{__CHANNEL__:__NICK__} __PARAM1__',
#      'MOTD'=>'',
#      'LUSERS'=>'',
#      'VERSION'=>'',
#      'STATS'=>'',
#      'LINKS'=>'',
#      'TIME'=>'',
#      'CONNECT'=>'',
#      'TRACE'=>'',
#      'ADMIN'=>'',
#      'INFO'=>'',
#      'SERVLIST'=>'',
#      'SQUERY'=>'',
#      'WHO'=>'',
#      'WHOIS'=>'',
#      'WHOWAS'=>'',
#      'KILL'=>'',
      'PING'=>'',
      'PONG'=>'',
      'ERROR'=>'__TIME__*** ERROR - __PARAM0__',
#      'AWAY'=>'',
#      'REHASH'=>'',
#      'DIE'=>'',
#      'RESTART'=>'',
#      'SUMMON'=>'',
#      'USERS'=>'',
#      'WALLOPS'=>'',
#      'USERHOST'=>'',
#      'ISON'=>'',
      '001'=>'__TIME__*** __TRAILING__',
      '002'=>'__TIME__*** __TRAILING__',
      '003'=>'__TIME__*** __TRAILING__',
      '004'=>'',
      '005'=>'',
      '250'=>'__TIME__*** __TRAILING__',
      '251'=>'__TIME__*** __TRAILING__',
      '254'=>'__TIME__*** __TRAILING__',
      '255'=>'__TIME__*** __TRAILING__',
      '265'=>'__TIME__*** __TRAILING__',
      '266'=>'__TIME__*** __TRAILING__',
      '301'=>'__TIME__*** __PARAM1__ is away: __TRAILING__',
#      '302'=>'',
      '311'=>'__TIME__*** __PARAM1__ is __TRAILING__ \(__PARAM2__@__PARAM3__\)',
      '312'=>'__TIME__*** on via server __PARAM2__: __TRAILING__',
      '317'=>'__TIME__*** __PARAM1__ is __PARAM2__ __TRAILING__',
      '319'=>'__TIME__*** __PARAM1__: __TRAILING__',
      '324'=>'__TIME__*** Mode for __CHANNEL__: __PARAM2__',
      '329'=>'',
      '332'=>'__TIME__*** Topic for __CHANNEL__: __TRAILING__',
      '333'=>'',
      '353'=>'__TIME__*** Users on __CHANNEL__: __TRAILING__',
      '366'=>'',
      '368'=>'',
      '372'=>'',
      '375'=>'__TIME__*** __TRAILING__',
      '376'=>'',
      '422'=>'__TIME__*** __TRAILING__',
      '433'=>'__TIME__*** __TRAILING__',
    }
    @@PrivmsgMap = {
      'ACTION'=>'__TIME__*** __CHANNEL__:__NICK__ __ACTION__',
    }
    @@MessageMap.default = "__TIME__*** FIXME *** __RAW__"

=begin rdoc

=== PRUNE::Decoration#extend_object(obj)

=end

    def self.extend_object(obj)
      class << obj
        alias_method :to_raw, :to_s
      end
      super
    end # def extend_object

=begin rdoc

=== PRUNE::Decoration#timeformat=(fmt)

=end

    def timeformat=(fmt)
      @timeformat = fmt
    end # def timeformat=

=begin rdoc

=== PRUNE::Decoration#timeformat

=end

    def timeformat
      defined?(@timeformat) ? @timeformat : "%H:%M:%S"
    end # def timeformat

=begin rdoc

=== PRUNE::Decoration#timestamp=(flag)

=end

    def timestamp=(flag)
      @timestamp = (flag == true)
    end # def timestamp=

=begin rdoc

=== PRUNE::Decoration#timestamp?

=end

    def timestamp?
      defined?(@timestamp) ? @timestamp : true
    end # def timestamp?

=begin rdoc

=== PRUNE::Decoration#inspect

=end

    def inspect
      self.to_s
    end # def inspect

=begin rdoc

=== PRUNE::Decoration#to_s

=end

    def to_s
      symbolmap = {
        '__TIME__'=>'_to_time',
        '__RAW__'=>'_to_raw',
        '__NICK__'=>'_to_nick',
        '__TRAILING__'=>'_to_trailing',
        '__USER__'=>'_to_user',
        '__HOST__'=>'_to_host',
        '__CHANNEL__'=>'_to_channel',
        '__PARAMS__'=>'_to_params',
        '__ACTION__'=>'_to_action',
      }
      (0..14).each do |i|
        sym = sprintf("__PARAM%d__", i)
        func = sprintf("_to_param%d", i)
        symbolmap[sym] = func
        sym = sprintf("__PARAMS%d__", i)
        func = sprintf("_to_params%d", i)
        symbolmap[sym] = func
      end
      msgfmt = nil
      data = nil
      if self.kind_of?(PRUNE::Message::PRIVMSG) then
        @@PrivmsgMap.each do |k, v|
          if self.params(1) =~ /\A\001#{k}/ then
            msgfmt = v
            data = k
            break
          end
        end
        msgfmt = @@MessageMap[self.command] if msgfmt.nil?
      else
        msgfmt = @@MessageMap[self.command]
      end

      return '' if msgfmt.nil? || msgfmt.empty?

      table = {}
      retval = nil
      @composer = PRUNE::MessageComposer.new unless defined?(@composer)
      Thread.exclusive do
        @composer.format = msgfmt
        @composer.each_symbol do |sym|
          table[sym] = PRUNE::TYPE::ComposeInfoStruct.new(PRUNE::MessageComposer::TYPE_OBJECT, self, symbolmap[sym], [data])
        end
        retval = sprintf("%s\n", @composer.compose(table))
      end

      retval
    end # def to_s

    private

    def _to_time(orig, sym, *data)
      orig.sub(sym, self.timestamp? ? sprintf("%s ", self.time.strftime(self.timeformat)) : "")
    end # def _to_time

    def _to_raw(orig, sym, *data)
      orig.sub(sym, sprintf("%s", self.to_raw))
    end # def _to_raw

    def _to_nick(orig, sym, *data)
      orig.sub(sym, sprintf("%s", self.nick))
    end # def _to_nick

    (0..14).each do |i|
      module_eval <<_E_, __FILE__, __LINE__
      def _to_param#{i}(orig, sym, *data)
        orig.sub(sym, sprintf("%s", self.params(#{i})))
      end

      def _to_params#{i}(orig, sym, *data)
        en = self.params(-1).nil? ? -2 : -1
        arg = self.params[#{i}..en]
        arg = arg.join(' ') unless arg.nil?
        orig.sub(sym, sprintf("%s", arg))
      end
_E_
    end

    def _to_trailing(orig, sym, *data)
      orig.sub(sym, sprintf("%s", self.params(-1)))
    end # def _to_trailing

    def _to_user(orig, sym, *data)
      orig.sub(sym, sprintf("%s", self.user))
    end # def _to_user

    def _to_host(orig, sym, *data)
      orig.sub(sym, sprintf("%s", self.host))
    end # def _to_host

    def _to_channel(orig, sym, *data)
      orig.sub(sym, sprintf("%s", self.channel({:suffix=>true})))
    end # def _to_channel

    def _to_params(orig, sym, *data)
      arg = self.params
      arg.join(' ') unless arg.nil?
      orig.sub(sym, sprintf("%s", arg))
    end # def _to_params

    def _to_action(orig, sym, *data)
      key = data[0]
      arg = self.params(1).sub(/\A\001#{key} /, '').sub(/\001\Z/, '')

      orig.sub(sym, sprintf("%s", arg))
    end # def _to_action

  end # module Decoration

end # module PRUNE
